import {isSameVNode} from "./index.js";

export function patch(oldVNode,newVNode){
    if(!oldVNode){
        return createEle(newVNode)
    }
    // 判断是否是一个真实元素,如果是真实DOM会返回1
    const isRealEle = oldVNode.nodeType;
    // 初次渲染
    if(isRealEle){
        // 获取真实元素
        const elm = oldVNode
        // 获取真实元素的父元素
        const parentElm = elm.parentNode
        // 创建新的虚拟节点
        let newRealEl = createEle(newVNode)
        // 把新的虚拟节点插入到真实元素后面
        parentElm.insertBefore(newRealEl,elm.nextSibling)
        // 然后删除之前的DOM
        parentElm.removeChild(elm)
        // 返回渲染后的虚拟节点
        return newRealEl
    }else{
        return patchVNode(oldVNode,newVNode)
    }
}

function createComponent(vnode){
    let i = vnode.data
    if((i=i.hook) && (i=i.init)){
        i(vnode)
    }
    if(vnode.componentsInstance){
        return true
    }
}

export function createEle(vnode){
    let {tag,data,children,text} = vnode
    if (typeof tag === "string"){
        // 判断是否是组件
        if(createComponent(vnode)){
            return vnode.componentsInstance.$el
        }
        vnode.el = document.createElement(tag)
        // 处理节点的属性
        patchProps(vnode.el,{},data)
        // 递归处理子元素
        children.forEach(child=>{
            child && vnode.el.appendChild(createEle(child))
        })
    }else{
        vnode.el = document.createTextNode(text)
    }
    return vnode.el
}

function patchProps(el,oldProps = {},props = {}){
    let oldPropsStyle = oldProps.style
    let newPropsStyle = props.style

    // 判断如果新节点上没有旧节点的样式，则应该吧原来的样式清空掉
    for (const key in oldPropsStyle) {
        if(!newPropsStyle[key]){
            el.style[key] = ""
        }
    }

    // 判断旧节点的属性在新节点是否存在
    for (const key in oldProps) {
        if(!props[key]){
            el.removeAttribute(key)
        }
    }

    for (const key in props) {
        if(key === "style"){
            Object.keys(props.style).forEach(sty=>{
                el.style[sty] = props.style[sty]
            })
        }else{
            el.setAttribute(key,props[key])
        }
    }
}

function patchVNode(oldVNode,newVNode){
    // 进行diff算法，对比新老节点
    // 对比两个节点的tag和key是否一样，如果不一样，则直接吧老的替换掉，换成新的节点
    if(!isSameVNode(oldVNode,newVNode)){
        console.log(oldVNode,'oldVNode')
        console.log(newVNode,'newVNode')
        let el = createEle(newVNode)
        oldVNode.el.parentNode.replaceChild(el,oldVNode.el)
        return el
    }
    // 一样的情况对DOM元素进行复用
    let el = newVNode.el = oldVNode.el
    // 如果一样，则还需要判断一下文本的情况
    if(!oldVNode.tag){
        if(oldVNode.text !== newVNode.text){
            el.textContent = newVNode.text
        }
    }
    // 比较新节点和旧节点的属性是否一致
    patchProps(el,oldVNode.data,newVNode.data)
    // 然后比较新旧节点的儿子节点
    let oldVNodeChildren = oldVNode.children || []
    let newVNodeChildren = newVNode.children || []

    if(oldVNodeChildren.length > 0 && newVNodeChildren.length > 0){
        // 声明两个指针，分别指向头节点和尾节点
        // 然后进行对比，当旧node的头节点和新node的头节点相同时，则进行头指针往后移动
        // 当头指针大于尾指针时停止循环
        let oldStartIndex = 0
        let oldEndIndex = oldVNodeChildren.length - 1
        let oldStartNode = oldVNodeChildren[0]
        let oldEndNode = oldVNodeChildren[oldEndIndex]

        let newStartIndex = 0
        let newEndIndex = newVNodeChildren.length - 1
        let newStartNode = newVNodeChildren[0]
        let newEndNode = newVNodeChildren[newEndIndex]

        // 添加一个映射表
        let nodeMap = {}
        oldVNodeChildren.forEach((child,index)=>{
            nodeMap[child.key] = index
        })

        while (oldStartIndex <= oldEndIndex && newStartIndex <= newEndIndex){
            if(!oldStartNode){
                oldStartNode = oldVNodeChildren[++oldStartIndex]
            }else if(!oldEndNode){
                oldEndNode = oldVNodeChildren[--oldEndIndex]
            }
            // 1.进行头头对比
            else if(isSameVNode(oldStartNode,newStartNode)){
                // 递归更新子节点
                patchVNode(oldStartNode,newStartNode)
                oldStartNode = oldVNodeChildren[++oldStartIndex]
                newStartNode = newVNodeChildren[++newStartIndex]
            }
            // 2.进行尾尾对比
            else if(isSameVNode(oldEndNode,newEndNode)){
                // 递归更新子节点
                patchVNode(oldEndNode,newEndNode)
                oldEndNode = oldVNodeChildren[--oldEndIndex]
                newEndNode = newVNodeChildren[--newEndIndex]
            }
            // 3.进行尾头
            else if(isSameVNode(oldEndNode,newStartNode)){
                patchVNode(oldEndNode,newStartNode)
                // 如果旧节点的尾节点和新节点的头节点相同，则吧把旧节点的尾节点放在头节点之前
                // 然后把旧的尾指针往前移动，新节点的头指针往后移动
                el.insertBefore(oldEndNode.el,oldStartNode.el)
                oldEndNode = oldVNodeChildren[--oldEndIndex]
                newStartNode = newVNodeChildren[++newStartIndex]
            }
            // 4.进行头尾对比
            else if(isSameVNode(oldStartNode,newEndNode)){
                patchVNode(oldStartNode,newEndNode)
                // 如果旧节点的头和新节点的尾相同，则吧旧节点的头节点放在尾节点后
                // 然后把旧的头指针往后移动，新节点的尾指针往前移动
                el.insertBefore(oldStartNode.el,oldEndNode.el.nextSibling)
                oldStartNode = oldVNodeChildren[++oldStartIndex]
                newEndNode = newVNodeChildren[--newEndIndex]
            }else{
                // 5.进行乱序查找
                let oldNodeIndex = nodeMap[newStartNode.key]
                if(oldNodeIndex !== undefined){
                    let moveNode = oldVNodeChildren[oldNodeIndex]
                    el.insertBefore(moveNode.el,oldStartNode.el)
                    oldVNodeChildren[oldNodeIndex] = undefined
                    patchVNode(moveNode,newStartNode)
                }else{
                    el.insertBefore(createEle(newStartNode),oldStartNode.el)
                }
                newStartNode = newVNodeChildren[++newStartIndex]
            }
        }

        // 循环结束后，如果新节点的头指针小于等于新节点的尾指针
        // 说明新节点是有多出来的内容，则要把新节点多出来的push到现有节点的后面
        if(newStartIndex <= newEndIndex){
            console.log("1")
            for (let i = newStartIndex; i <= newEndIndex; i++) {
                // 如果尾指针的下一个节点有值，说明是新节点的前面有多出来的节点
                // 需要吧新的节点插入到前面去
                let anchor = newVNodeChildren[newEndIndex + 1] ? newVNodeChildren[newEndIndex + 1].el : null
                // 吧新节点插入到anchor的前面
                el.insertBefore(createEle(newVNodeChildren[i]),anchor)
            }
        }
        // 如果旧节点的头指针小于等于旧节点的尾指针，则说明旧的有多的节点，需要删除掉
        if(oldStartIndex <= oldEndIndex){
            for (let i = oldStartIndex; i <= oldEndIndex; i++) {
               let chilEl = oldVNodeChildren[i]
               chilEl && el.removeChild(chilEl.el)
            }
        }
    }else if(newVNodeChildren.length > 0){
        // 这里表示老节点没有儿子，但是新节点有，需要遍历新节点的每一个儿子，放在新节点中
        mountChildren(el,newVNodeChildren)
    }else if(oldVNodeChildren.length > 0){
        // 这里表示新节点没有儿子，但是老节点有，需要吧老节点的儿子删除掉
        unMountChildren(el,oldVNodeChildren)
    }
    return el
}

function mountChildren(el,children){
    for (const child of children) {
        el.appendChild(createEle(child))
    }
}

function unMountChildren(el,children){
    // 直接删除老节点的子元素
    el.innerHTML = ""
}
